<?php
/**
 * DMFramework
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @url http://dmframework.nl
 */

/**
 * The autoloader is almost the most important class since it automaticly loads files
 * when a class name is called, you can even add custom paths (with prefixes).
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @package System.Core
 */
class Dmf_Autoloader
{
    /**
     * All paths
     * 
     * @var array 
     */
    private $_paths = array();
    
    /**
     * The default controller directory
     * 
     * @var string
     */
    private $_controller_directory = 'application/controllers/';
    
    /**
     * The default models directory
     * 
     * @var string
     */
    private $_model_directory = 'application/models/';
    
    /**
     * The default form directory
     * 
     * @var string
     */
    private $_form_directory = 'application/forms/';
    
    /**
     * The default widget directory
     * 
     * @var string
     */
    private $_widget_directory = 'widgets/';
    
    /**
     * The autoloader instance
     * 
     * @var Dmf_Autoloader
     */
    private static $_instance;
    
    /**
     * Get the autoloader instance
     *  
     * @return Dmf_Autoloader
     */
    public static function getInstance()
    {
        if ( ! self::$_instance instanceof Dmf_Autoloader )
        {
            self::$_instance = new Dmf_Autoloader;
        }
        
        return self::$_instance;
    }
    
    /**
     * Set the controller directory
     * 
     * @param string $dir
     * @throws Dmf_Exception_FileNotFound 
     */
    public function setControllerDirectory( $dir )
    {
        if ( ! is_dir( $dir ) )
        {
            throw new Dmf_Exception_FileNotFound( 'The directory: "' . $dir . '" is not found or isn\'t a directory.' );
        }
        
        $this->_controller_directory = $dir;
    }
    
    /**
     * Set the model directory
     * 
     * @param string $dir
     * @throws Dmf_Exception_FileNotFound 
     */
    public function setModelDirectory( $dir )
    {
        if ( ! is_dir( $dir ) )
        {
            throw new Dmf_Exception_FileNotFound( 'The directory: "' . $dir . '" is not found or isn\'t a directory.' );
        }
        
        $this->_model_directory = $dir;
    }
    
    /**
     * Set the form directory
     * 
     * @param string $dir
     * @throws Dmf_Exception_FileNotFound 
     */
    public function setFormDirectory( $dir )
    {
        if ( ! is_dir( $dir ) )
        {
            throw new Dmf_Exception_FileNotFound( 'The directory: "' . $dir . '" is not found or isn\'t a directory.' );
        }
        
        $this->_form_directory = $dir;
    }
    
    /**
     * Set the widget directory
     * 
     * @param string $dir
     * @throws Dmf_Exception_FileNotFound 
     */
    public function setWidgetDirectory( $dir )
    {
        if ( ! is_dir( $dir ) )
        {
            throw new Dmf_Exception_FileNotFound( 'The directory: "' . $dir . '" is not found or isn\'t a directory.' );
        }
        
        $this->_widget_directory = $dir;
    }
    
    /**
     * Add a path to the autoloader
     * 
     * @param string $prefix
     * @param string $path
     * @return null
     */
    public function addPath( $prefix, $path )
    {
        if ( is_array( $path ) )
        {
            foreach( $path as $aPath )
            {
                $this->addPath( $prefix, $aPath );
            }
            
            return;
        }
        
        $this->_paths[] = array( $prefix, $path );
    }
    
    /**
     * Remove a path from the autoloader
     * 
     * @param string $prefix 
     */
    public function removePath( $prefix )
    {
        foreach( $this->_paths as &$path )
        {
            if ( $path['0'] == $prefix ) $path = null;
        }
        
        $this->_paths = array_values( array_filter( $this->_paths ) );
    }
    
    /**
     * Load class
     * 
     * @param string $filename
     * @param string $classname 
     */
    public function loadFile( $filename, $classname )
    {
        return file_exists( $filename ) && ( include( $filename ) ) && class_exists( $classname, false );
    }
    
    /**
     * Get the filename
     * 
     * @param string $classname
     * @return string 
     */
    public function getFilename( $classname )
    {
        $filename = explode( '_', strtr( $classname, array(
            'Dmf_' => ''
        ) ) );
        $filenameLength = count( $filename );
        
        for ( $i = 0; $i < ( $filenameLength - 1 ); ++$i )
        {
            $filename[ $i ] = strtolower( $filename[ $i ] );
        }
        
        $filename[ count( $filename ) - 1 ] = ucfirst( $filename[ count( $filename ) - 1 ] );
        $filename = implode( '/', $filename ) . '.php';
        
        return $filename;
    }
    
    /**
     * Load a class
     * 
     * @param string $classname
     * @throws Exception 
     */
    public function loadClass( $classname )
    {
        if ( strstr( $classname, 'Controller' ) && $this->loadFile( $this->_controller_directory . $classname . '.php', $classname ) ) return;
        if ( strstr( $classname, 'Model' ) && $this->loadFile( $this->_model_directory . $classname . '.php', $classname ) ) return;
        if ( strstr( $classname, 'Form' ) && $this->loadFile( $this->_form_directory . $classname . '.php', $classname ) ) return;
        if ( strstr( $classname, 'Widget' ) && $this->loadFile( $this->_widget_directory . ucfirst( substr( $classname, 0, -6 ) ) . '.php',  ucfirst( $classname ) ) ) return;
        
        $filename = $this->getFilename( $classname );
        
        if ( $this->loadFile( 'system/core/' . $filename, $classname ) ) return;
        if ( $this->loadFile( 'system/library/' . $filename, $classname ) ) return;
        if ( $this->loadFile( 'application/library/' . $filename, $classname ) ) return;
        
        foreach( $this->_paths as $path )
        {
            if ( strstr( $classname, $path['0'] ) && file_exists( $path['1'] . '/' . $classname . '.php' ) && ( include( $path['1'] . '/' . $classname . '.php' ) ) && class_exists( $classname ) ) return;
        }
        
        throw new Dmf_Exception_ClassNotFound( 'Class: ' . $classname . ' could not be found' );
    }
}